// axios配置  可自行根据项目进行更改，只需更改该文件即可，其他文件可以不动

import { AxiosResponse } from 'axios'
import { AxiosTransform, CreateAxiosOptions } from './axiosTransform'
import { ContentTypeEnum, RequestEnum, ResultEnum } from '@/constants'
import { messageError, modalError } from '@/hooks'
import { appendUrlParams, clone, deepMerge, isFunction, isString } from '@/utils'
import { formatRequestDate, joinTimestamp } from './helper'
import { checkStatus } from './checkStatus'
import { IAxios } from './Axios'
import { useUserStoreWithout } from '@/store/modules/user'
type RequestOptions = AxiosType.RequestOptions
type RequestResult = AxiosType.RequestResult

/**
 * @description: 数据处理，方便区分多种处理方式
 */
const transform: AxiosTransform = {
	/**
	 * @description: 处理请求数据。如果数据不是预期格式，可直接抛出错误
	 */
	transformRequestHook: (res: AxiosResponse<RequestResult>, options: RequestOptions) => {
		const { isTransformResponse, isReturnNativeResponse } = options
		// 是否返回原生响应头 比如：需要获取响应头时使用该属性
		if (isReturnNativeResponse) {
			return res
		}

		// 不进行任何处理，直接返回
		// 用于页面代码可能需要直接获取code，data，message这些信息时开启
		if (!isTransformResponse) {
			return res.data
		}
		// 错误的时候返回

		const { data } = res
		if (!data) {
			// return '[HTTP] Request has no return value';
			throw new Error('请求出错，请稍候重试')
		}
		//  这里 code，result，message为 后台统一的字段
		const { code, result, message } = data

		// 这里逻辑可以根据项目进行修改
		const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS

		if (hasSuccess) {
			return result
		}

		let timeoutMsg = ''
		switch (code) {
			case ResultEnum.TIMEOUT:
				timeoutMsg = '请求超时'
				//可添加请求超时相关操作
				break
			default:
				if (message) {
					timeoutMsg = message
				}
		}

		if (options.errorMessageMode === 'modal') {
			modalError({ title: '错误提示', message: timeoutMsg })
		} else if (options.errorMessageMode === 'message') {
			messageError(timeoutMsg)
		}

		throw new Error(timeoutMsg || '请求失败')
	},

	// 请求之前处理config
	beforeRequestHook: (config, options) => {
		const { apiUrl, joinParamsToUrl, formatDate, joinTime = true } = options

		if (apiUrl) {
			const _apuUrl = isString(apiUrl) ? apiUrl : isFunction(apiUrl) ? apiUrl?.() : ''
			config.url = `${_apuUrl}${config.url}`
		}
		const params = config.params || {}
		const data = config.data || false
		formatDate && data && !isString(data) && formatRequestDate(data)
		if (config.method?.toUpperCase() === RequestEnum.GET) {
			if (!isString(params)) {
				// 给 get 请求加上时间戳参数，避免从缓存中拿数据。
				config.params = Object.assign(params || {}, joinTimestamp(joinTime, false))
			} else {
				// 兼容restful风格
				config.url = config.url + params + `${joinTimestamp(joinTime, true)}`
				config.params = undefined
			}
		} else {
			if (!isString(params)) {
				formatDate && formatRequestDate(params)
				if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
					config.data = data
					config.params = params
				} else {
					// 非GET请求如果没有提供data，则将params视为data
					config.data = params
					config.params = undefined
				}
				if (joinParamsToUrl) {
					config.url = appendUrlParams(config.url as string, Object.assign({}, config.params, config.data))
				}
			} else {
				// 兼容restful风格
				config.url = config.url + params
				config.params = undefined
			}
		}
		return config
	},

	/**
	 * @description: 请求拦截器处理
	 */
	requestInterceptors: (config, options) => {
		// 请求之前处理config
		const userStore = useUserStoreWithout()
		const token = userStore.accessToken
		if (token && (config as Recordable<any>)?.requestOptions?.withToken !== false) {
			// eslint-disable-next-line no-extra-semi
			;(config as Recordable<any>).headers.Authorization = options.authenticationScheme
				? `${options.authenticationScheme} ${token}`
				: token
		}
		return config
	},

	/**
	 * @description: 响应拦截器处理
	 */
	responseInterceptors: (res: AxiosResponse<any>) => {
		return res
	},

	/**
	 * @description: 响应错误处理
	 */
	responseInterceptorsCatch: (error: any) => {
		const { response, code, message, config } = error || {}
		const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'
		const msg: string = response?.data?.error?.message ?? ''
		const err: string = error?.toString?.() ?? ''
		let errMessage = ''

		try {
			if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
				errMessage = '接口请求超时,请刷新页面重试!'
			}
			if (err?.includes('Network Error')) {
				errMessage = '网络异常，请检查您的网络连接是否正常!'
			}

			if (errMessage) {
				if (errorMessageMode === 'modal') {
					modalError({ title: '错误提示', message: errMessage })
				} else if (errorMessageMode === 'message') {
					messageError(errMessage)
				}
				return Promise.reject(error)
			}
		} catch (error) {
			throw new Error(error as unknown as string)
		}

		checkStatus(error?.response?.status, msg, errorMessageMode)
		return Promise.reject(error)
	}
}

export const createAxios = (opt?: Partial<CreateAxiosOptions>) => {
	return new IAxios(
		deepMerge(
			{
				// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
				// authentication schemes，e.g: Bearer
				// authenticationScheme: 'Bearer',
				authenticationScheme: '',
				timeout: 10 * 1000,
				// 基础接口地址
				baseURL: import.meta.env.VITE_BASE_URL,

				headers: { 'Content-Type': ContentTypeEnum.JSON },
				// 如果是form-data格式
				// headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
				// 数据处理方式
				transform: clone(transform),
				// 配置项，下面的选项都可以在独立的接口请求中覆盖
				requestOptions: {
					// 是否返回原生响应头 比如：需要获取响应头时使用该属性
					isReturnNativeResponse: false,
					// 需要对返回数据进行处理
					isTransformResponse: true,
					// post请求的时候添加参数到url
					joinParamsToUrl: false,
					// 格式化提交参数时间
					formatDate: true,
					// 消息提示类型
					errorMessageMode: 'message',
					//  是否加入时间戳
					joinTime: true,
					// 忽略重复请求
					ignoreCancelToken: true,
					// 是否携带token
					withToken: true
				}
			},
			opt || {}
		)
	)
}

export const request = createAxios()

// other api url
// export const otherHttp = createAxios({
//   requestOptions: {
//     apiUrl: 'xxx',
//   },
// });
